﻿using System.Linq;
using System.Modules.Contracts.DTO;
using System.Modules.SharePoint.Contracts.Attributes;
using System.Modules.SharePoint.Contracts.DTO;
using System.Reflection;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;

namespace System.Modules.SharePoint.Extensions
{
    /// <summary>
    /// SPWorkflowTaskPropertiesExtensions
    /// </summary>
    public static class SPWorkflowTaskPropertiesExtensions
    {
        /// <summary>
        /// This method loads SPWorkflowTaskProperties into a T type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="task">SPListItem</param>
        /// <param name="workflowProperties"> Workflow Properties </param>
        /// <returns>T</returns>
        public static SPWorkflowTaskActivityProperties<T> LoadTask<T>(this SPWorkflowTaskProperties task, SPWorkflowActivationProperties workflowProperties) where T : new()
        {
            var value = new SPWorkflowTaskActivityProperties<T>();
            if (task != null)
            {
                value.ID = task.TaskItemId;
                value.Title = task.Title;
                value.AssignedTo = task.AssignedTo;
                value.Description = task.Description;
                value.DueDate = task.DueDate;
                value.StartDate = task.StartDate;
                value.PercentComplete = task.PercentComplete;
                Type type = typeof (T);
                PropertyInfo[] properties = type.GetProperties();
                SPFieldCollection fields = workflowProperties.TaskList.Fields;
                foreach (PropertyInfo property in properties)
                {
                    var bindingProperty = property.GetCustomAttributes(typeof (BindingSPField), false).FirstOrDefault() as BindingSPField;
                    string propertyName = bindingProperty != null && !String.IsNullOrEmpty(bindingProperty.Name) ? bindingProperty.Name : property.Name;
                    if (fields.ContainsField(propertyName))
                    {
                        SPField field = fields.GetFieldByInternalName(propertyName);
                        if (task.ExtendedProperties.ContainsKey(field.Id) && property.CanWrite)
                        {
                            object fieldValue = task.ExtendedProperties[field.Id];
                            if (bindingProperty != null && bindingProperty.Function != null)
                            {
                                fieldValue = bindingProperty.Function.Invoke(fieldValue);
                            }
                            if (fieldValue != null)
                            {
                                if (fieldValue.GetType() == property.PropertyType)
                                {
                                    property.SetValue(value.Extended.Value, fieldValue, null);
                                }
                                else if (property.PropertyType.IsGenericType && property.PropertyType.IsSubclassOf(typeof (Updatable)))
                                {
                                    var updatable = Activator.CreateInstance(property.PropertyType) as Updatable;
                                    if (updatable != null)
                                    {
                                        updatable.SetValue(Convert.ChangeType(fieldValue, updatable.GetUnderlyingType()));
                                        property.SetValue(value.Extended.Value, updatable, null);
                                    }
                                }
                                else
                                {
                                    property.SetValue(value.Extended.Value, Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType), null);
                                }
                            }
                        }
                    }
                }
            }
            return value;
        }

        /// <summary>
        /// Bind Task
        /// </summary>
        /// <param name="task"></param>
        /// <param name="workflowProperties"> </param>
        /// <param name="value"></param>
        /// <typeparam name="T"></typeparam>
        public static void BindTask<T>(this SPWorkflowTaskProperties task, SPWorkflowActivationProperties workflowProperties, SPWorkflowTaskActivityProperties<T> value) where T : new()
        {
            if (task != null && !Equals(default(T), value))
            {
                if (value.ID != null && value.ID.IsUpdated)
                {
                    task.TaskItemId = value.ID.Value;
                }
                if (value.Title != null && value.Title.IsUpdated)
                {
                    task.Title = value.Title.Value;
                }
                if (value.Description != null && value.Description.IsUpdated)
                {
                    task.Description = value.Description.Value;
                }
                if (value.AssignedTo != null && value.AssignedTo.IsUpdated)
                {
                    task.AssignedTo = value.AssignedTo.Value;
                }
                if (value.DueDate != null && value.DueDate.IsUpdated)
                {
                    task.DueDate = value.DueDate.Value;
                }
                if (value.StartDate != null && value.StartDate.IsUpdated)
                {
                    task.StartDate = value.StartDate.Value;
                }
                if (value.PercentComplete != null && value.PercentComplete.IsUpdated)
                {
                    task.PercentComplete = value.PercentComplete.Value;
                }
                if (value.Extended != null && value.Extended.HasValue)
                {
                    Type type = typeof (T);
                    PropertyInfo[] properties = type.GetProperties();
                    SPFieldCollection fields = workflowProperties.TaskList.Fields;
                    foreach (PropertyInfo property in properties)
                    {
                        string propertyName = property.Name;
                        if (fields.ContainsField(propertyName))
                        {
                            SPField field = fields.GetFieldByInternalName(propertyName);
                            if (field != null)
                            {
                                if (property.PropertyType.IsGenericType && property.PropertyType.IsSubclassOf(typeof (Updatable)))
                                {
                                    var updatable = property.GetValue(value.Extended.Value, null) as Updatable;
                                    if (updatable != null && updatable.IsUpdated)
                                    {
                                        task.ExtendedProperties[field.Id] = updatable.GetValue();
                                    }
                                }
                                else
                                {
                                    task.ExtendedProperties[field.Id] = property.GetValue(value.Extended.Value, null);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}